/*
 * Copyright (c) 2021 MediaTek Inc.
 *
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file or at
 * https://opensource.org/licenses/MIT
 */

#include <debug.h>
#include <platform/pll.h>
#include <platform/reg.h>
#include <platform/wait.h>

const char *abist_array[] = {
    "AD_ADSPPLL_CK",
    "AD_APLL1_CK",
    "AD_APLL2_CK",
    "AD_APLL3_CK",
    "AD_APLL4_CK",
    "AD_APLL5_CK",
    "AD_DGIPLL_CK",
    "pad_in_dgi_clk_i",
    "AD_HDMIPLL1_CK",
    "AD_HDMIPLL2_CK",
    "AD_HDMIRX_APLL_CK",
    "AD_ETHPLL_CK",
    "AD_IMG_PLL_CK",
    "AD_MAINPLL_CK",
    "AD_MAINPLL_DIV2_CK",
    "AD_MAINPLL_DIV3_CK",
    "AD_MAINPLL_DIV4_CK",
    "AD_MAINPLL_DIV5_CK",
    "AD_MAINPLL_DIV6_CK",
    "AD_MAINPLL_DIV7_CK",
    "AD_MAINPLL_DIV9_CK",
    "AD_CLKSQ_FS26M_CK",
    "AD_MMPLL_CK",
    "AD_MMPLL_D3_CK",
    "AD_MMPLL_D4_CK",
    "AD_MMPLL_D5_CK",
    "AD_MMPLL_D6_CK",
    "AD_MMPLL_D7_CK",
    "AD_MMPLL_D9_CK",
    "AD_MPLL_CK",
    "AD_MSDCPLL_CK",
    "AD_MFG_PLL_CK",
    "AD_MFG_PLL_OPP_CK",
    "AD_TVDPLL1_CK",
    "AD_TVDPLL2_CK",
    "AD_CLKSQ_CKBUF1_26M",
    "AD_CLKSQ_CKBUF2_26M",
    "AD_UNIVPLL_CK",
    "AD_UNIVPLL_DIV2_CK",
    "AD_UNIVPLL_DIV3_CK",
    "AD_UNIVPLL_DIV4_CK",
    "AD_UNIVPLL_DIV5_CK",
    "AD_UNIVPLL_DIV6_CK",
    "AD_UNIVPLL_DIV7_CK",
    "AD_USB20_192M_CK",
    "AD_USB1PLL_CK",
    "AD_VDECPLL_CK",
    "AD_ULPOSC_CK",
    "AD_OSC_SYNC_CK",
    "AD_ULPOSC2_CK",
    "AD_OSC_SYNC_CK_2",
    "AD_ARMPLL_LL_CK",
    "AD_ARMPLL_BL_CK",
    "AD_CCIPLL_CK",
    "AD_NNAPLL_CK",
    "AD_RESPLL_CK",
    "AD_APPLLGP1_MON_FM_CK",
    "dsi0_ad_dsi_ckg_dsiclk_anaout",
    "dsi0_ad_dsi_test_ck_anaout",
    "dsi1_ad_dsi_ckg_dsiclk_anaout",
    "dsi1_ad_dsi_test_ck_anaout",
    "_FIXED_0",
    "AD_NNAPLL_CKDIV_CK",
    "ADA_LVTS_TO_PLLGP_MON_CK_L1",
    "ADA_LVTS_TO_PLLGP_MON_CK_L2",
    "ADA_LVTS_TO_PLLGP_MON_CK_L3",
    "ADA_LVTS_TO_PLLGP_MON_CK_L4",
    "ADA_LVTS_TO_PLLGP_MON_CK_L5",
    "ADA_LVTS_TO_PLLGP_MON_CK_L6",
    "ADA_LVTS_TO_PLLGP_MON_CK_L7",
    "AD_CSI0A_CDPHY_DELAYCAL_CK",
    "AD_CSI0B_CDPHY_DELAYCAL_CK",
    "AD_CSI0C_CDPHY_DELAYCAL_CK",
    "AD_CSI0D_CDPHY_DELAYCAL_CK",
    "AD_CSI2A_CDPHY_DELAYCAL_CK",
    "AD_CSI2B_CDPHY_DELAYCAL_CK",
    "eARC_bck_int",
    "mclk_128fs_o",
    "spdifin_iec_ck",
    "other_splin_bck_i",
    "other_splin_mck_i",
    "u2a_rx_sof_p0",
    "u2a_rx_sof_p2",
    "u2a_rx_sof_p3",
    "u2a_tx_sof_p0",
    "u2a_tx_sof_p1",
    "u2a_tx_sof_p2",
    "u2a_tx_sof_p3",
    "aud_i2sin_mck_i",
    "aud_i2sin_bck_i",
    "other_tdmin_mck_i",
    "aud_i2so2_bck_i",
    "aud_i2si4_bck_i",
    "aud_i2si5_bck_i",
    "AD_HDMITX21PLL_MONREF_CK",
    "AD_HDMITX21PLL_MONFBK_CK",
    "AD_HDMITX21_CLKDIG_1",
    "AD_HDMITX21_CLKDIG_1_CTS",
    "AD_HDMITXPLL_PIXEL_CK",
    "AD_EARCRX_DM_CK",
    "AD_EARCRX_CM_SLEW_CK",
    "AD_EARCRXPLL_CK_DIG",
    "msdc01_in_ck",
    "msdc02_in_ck",
    "msdc11_in_ck",
    "msdc12_in_ck",
    "msdc21_in_ck",
    "msdc22_in_ck",
    "msdc31_in_ck",
    "msdc32_in_ck",
    "UFS_MP_CLK2FREQ",
    "mcusys_arm_clk_out_all",
    "fmem_ck_aft_dcm_ch2",
    "fmem_ck_bfe_dcm_ch2",
    "AD_RPHYPLL_DIV4_CK_ch23",
    "AD_RCLRPLL_DIV4_CK_ch23",
    "fmem_ck_aft_dcm_ch0",
    "fmem_ck_bfe_dcm_ch0",
    "AD_RCLRPLL_DIV4_CK_ch01",
    "AD_RPHYPLL_DIV4_CK_ch01",
    "_FIXED_1",
    "_FIXED_2",
    "_FIXED_3",
    "_FIXED_4",
    "_FIXED_5",
    "_FIXED_6",
    "o_rx_acr_aud_clk_x",
};

const char *ckgen_array[] = {
    "hd_faxi_ck",
    "hg_fspm_ck",
    "hf_fscp_ck",
    "hd_fbus_aximem_ck",
    "hf_fvpp_ck",
    "hf_fethdr_ck",
    "hf_fipe_ck",
    "hf_fcam_ck",
    "hf_fccu_ck",
    "hf_fimg_ck",
    "hf_fcamtm_ck",
    "hf_fdsp_ck",
    "hf_fdsp1_ck",
    "hf_fdsp2_ck",
    "hf_fdsp3_ck",
    "hf_fdsp4_ck",
    "hf_fdsp5_ck",
    "hf_fdsp6_ck",
    "hf_fdsp7_ck",
    "hf_fipu_if_ck",
    "hf_fmfg_core_tmp_ck",
    "f_fcamtg_ck",
    "f_fcamtg2_ck",
    "f_fcamtg3_ck",
    "f_fcamtg4_ck",
    "f_fcamtg5_ck",
    "f_fuart_ck",
    "hf_fspi_ck",
    "hf_fspis_ck",
    "hf_fmsdc50_0_hclk_ck",
    "hf_fmsdc50_0_ck",
    "hf_fmsdc30_1_ck",
    "hf_fmsdc30_2_ck",
    "hf_fintdir_ck",
    "hf_faud_intbus_ck",
    "hf_faudio_h_ck",
    "f_fpwrap_ulposc_ck",
    "hf_fatb_ck",
    "hf_fpwrmcu_ck",
    "hf_fdp_ck",
    "hf_fedp_ck",
    "hf_fdpi_ck",
    "f_fdisp_pwm0_ck",
    "f_fdisp_pwm1_ck",
    "f_fusb_top_ck",
    "f_fssusb_xhci_ck",
    "f_fusb_top_1p_ck",
    "f_fssusb_xhci_1p_ck",
    "f_fusb_top_2p_ck",
    "f_fssusb_xhci_2p_ck",
    "f_fusb_top_3p_ck",
    "f_fssusb_xhci_3p_ck",
    "f_fi2c_ck",
    "f_fseninf_ck",
    "f_fseninf1_ck",
    "f_fseninf2_ck",
    "f_fseninf3_ck",
    "hf_gcpu_ck",
    "hf_fdxcc_ck",
    "hf_fdpmaif_main_ck",
    "hf_faes_ufsfde_ck",
    "hf_fufs_ck",
    "f_ufs_tick1us_ck",
    "f_ufs_mp_sap_cfg_ck",
    "hf_fvenc_ck",
    "hf_fvdec_ck",
    "hf_fpwm_ck",
    "hg_mcupm_ck",
    "hf_fspmi_p_mst_ck",
    "hf_fspmi_m_mst_ck",
    "hg_fdvfsrc_ck",
    "hf_ftl_ck",
    "hf_ftl_p1_ck",
    "hf_faes_msdcfde_ck",
    "hf_dsi_occ_ck",
    "hf_fwpe_vpp_ck",
    "hf_fhdcp_ck",
    "hf_fhdcp_24m_ck",
    "hf_fhd20_dacr_ref_clk",
    "hf_fhd20_hdcp_cclk",
    "hf_fhdmi_xtal",
    "hf_fhdmi_apb_ck",
    "hf_fsnps_eth_250m_ck",
    "hf_fsnps_eth_62p4m_ptp_ck",
    "hf_fsnps_eth_50m_rmii_ck",
    "hf_dgi_out_ck",
    "hf_ipnna_nna0_ck",
    "hf_ipnna_nna1_ck",
    "hf_fadsp_ck",
    "hf_fasm_h_ck",
    "hf_fasm_m_ck",
    "hf_fasm_l_ck",
    "hf_fapll1_ck",
    "hf_fapll2_ck",
    "hf_fapll3_ck",
    "hf_fapll4_ck",
    "hf_fapll5_ck",
    "hf_fi2so1_mck",
    "hf_fi2so2_mck",
    "hf_fi2so4_mck",
    "hf_fi2so5_mck",
    "hf_fi2si1_mck",
    "hf_fi2si2_mck",
    "hf_fi2si4_mck",
    "hf_fi2si5_mck",
    "hf_fdptx_mck",
    "hf_aud_iec_clk",
    "hf_a1sys_hp_ck",
    "hf_a2sys_hf_ck",
    "hf_a3sys_hf_ck",
    "hf_a4sys_hf_ck",
    "hf_fspinfi_bclk_ck",
    "hf_fnfi1x_ck",
    "hf_ecc_ck",
    "hf_faudio_local_bus_ck",
    "hf_fspinor_ck",
    "f_dvio_dgi_ref_ck",
    "f_ulposc_ck",
    "f_ulposc_core_ck",
    "hf_fsrck_ck",
    "src_occ_50m_clk",
    "src_occ_78m_clk",
    "src_occ_125m_clk",
    "src_occ_250m_clk",
    "src_occ_500m_clk",
    "src_occ_240m_clk",
    "src_occ_810m_clk",
    "src_occ_202m_clk",
    "src_occ_600m_clk",
    "src_occ_100m_clk",
    "src_occ_150m_clk",
    "src_occ_600m_1_clk",
    "src_occ_208m_clk",
    "src_occ_202m_1_clk",
    "src_occ_148m_clk",
    "src_occ_156m_clk",
    "src_occ_312m_clk",
    "src_occ_594m_clk",
    "src_occ_313m_clk",
    "src_occ_108m_clk",
    "src_occ_300m_clk",
    "src_occ_375m_clk",
    "src_occ_333m_clk",
    "src_occ_600m_2_clk",
    "src_occ_660m_clk",
    "src_occ_750m_clk",
    "src_occ_864m_clk",
    "src_occ_gcpu_div2_ck",
    "f_rsvd1_ck",
    "f_rsvd2_ck",
    "f_rsvd3_ck",
};

unsigned int mt_get_freq(unsigned int id, enum freq_type type)
{
    unsigned int clk_dbg_cfg, clk_misc_cfg_0, clk26cali_0, clk26cali_1;
    unsigned int shift, i = 0, output;
    bool timeout = false;

    /* Save default value */
    clk_dbg_cfg = read32(&mtk_topckgen->clk_dbg_cfg);
    clk_misc_cfg_0 = read32(&mtk_topckgen->clk_misc_cfg_0);
    clk26cali_0 = read32(&mtk_topckgen->clk26cali_0);
    clk26cali_1 = read32(&mtk_topckgen->clk26cali_1);

    shift = (type == CKGEN) ? 16 : 8;
    clrsetbits32(&mtk_topckgen->clk_dbg_cfg,
                 ((type == CKGEN) ? 0xff : 0x7f) << shift | (0x3) | (1 << 24),
                 (id << shift) | ((type == CKGEN) ? 1 : 0) | ((type == CKGEN) ? 1 : 0) << 24);
    clrsetbits32(&mtk_topckgen->clk_misc_cfg_0, 0xff << 24,
                 ((type == CKGEN) ? 0 : 0x3) << 24);
    write32(&mtk_topckgen->clk26cali_0, 0x80);
    write32(&mtk_topckgen->clk26cali_0, 0x90);
    /* Wait frequency meter finish */
    timeout = wait_ms(((read32(&mtk_topckgen->clk26cali_0) & 0x10) == 0), 100) == 0;

    if (!timeout) {
        output = (read32(&mtk_topckgen->clk26cali_1) & 0xffff) * 26000 / 1024;
        if (type != CKGEN)
            output *= 4;
    } else {
        output = 0;
    }

    /* Restore default value */
    write32(&mtk_topckgen->clk_dbg_cfg, clk_dbg_cfg);
    write32(&mtk_topckgen->clk_misc_cfg_0, clk_misc_cfg_0);
    write32(&mtk_topckgen->clk26cali_0, clk26cali_0);
    write32(&mtk_topckgen->clk26cali_1, clk26cali_1);

    return output;
}

void mt_fmeter_dump(void)
{
    unsigned int i;

    for (i = 1; i <= countof(abist_array); i++)
        dprintf(CRITICAL, "[abist][%u] %s: %u KHz\n", i, abist_array[i - 1],
                mt_get_freq(i, ABIST));

    for (i = 0; i < countof(ckgen_array); i++)
        dprintf(CRITICAL, "[ckgen][%u] %s: %u KHz\n", i, ckgen_array[i],
                mt_get_freq(i, CKGEN));
}
